hvm: Clean up EPT/NPT 'nested page fault' handling.
authorKeir Fraser <keir.fraser@citrix.com>
Mon, 26 Oct 2009 13:19:33 +0000 (13:19 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Mon, 26 Oct 2009 13:19:33 +0000 (13:19 +0000)
Share most of the code.

Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
xen/arch/x86/hvm/hvm.c
xen/arch/x86/hvm/svm/svm.c
xen/arch/x86/hvm/vmx/vmx.c
xen/include/asm-x86/hvm/hvm.h
xen/include/asm-x86/hvm/vmx/vmx.h

index 03798827d0e391c43bf82ef9e2b6aa9fa3c6d4d2..8a14a8e13e6432408207d20130e1ae9e0f449b4d 100644 (file)
@@ -916,6 +916,44 @@ void hvm_triple_fault(void)
     domain_shutdown(v->domain, SHUTDOWN_reboot);
 }
 
+bool_t hvm_hap_nested_page_fault(unsigned long gfn)
+{
+    p2m_type_t p2mt;
+    mfn_t mfn;
+
+    mfn = gfn_to_mfn_type_current(gfn, &p2mt, p2m_guest);
+
+    /*
+     * If this GFN is emulated MMIO or marked as read-only, pass the fault
+     * to the mmio handler.
+     */
+    if ( p2m_is_mmio(p2mt) || (p2mt == p2m_ram_ro) )
+    {
+        if ( !handle_mmio() )
+            hvm_inject_exception(TRAP_gp_fault, 0, 0);
+        return 1;
+    }
+
+    /* Log-dirty: mark the page dirty and let the guest write it again */
+    if ( p2mt == p2m_ram_logdirty )
+    {
+        paging_mark_dirty(current->domain, mfn_x(mfn));
+        p2m_change_type(current->domain, gfn, p2m_ram_logdirty, p2m_ram_rw);
+        return 1;
+    }
+
+    /* Shouldn't happen: Maybe the guest was writing to a r/o grant mapping? */
+    if ( p2mt == p2m_grant_map_ro )
+    {
+        gdprintk(XENLOG_WARNING,
+                 "trying to write to read-only grant mapping\n");
+        hvm_inject_exception(TRAP_gp_fault, 0, 0);
+        return 1;
+    }
+
+    return 0;
+}
+
 int hvm_set_efer(uint64_t value)
 {
     struct vcpu *v = current;
index c5fc6a9c9e45ca7e0c3353c5ba05a6a707768069..99325770c179ef46143b9aae8dde9f31c7fdf680 100644 (file)
@@ -884,46 +884,20 @@ void start_svm(struct cpuinfo_x86 *c)
     hvm_enable(&svm_function_table);
 }
 
-static void svm_do_nested_pgfault(paddr_t gpa, struct cpu_user_regs *regs)
+static void svm_do_nested_pgfault(paddr_t gpa)
 {
-    p2m_type_t p2mt;
-    mfn_t mfn;
     unsigned long gfn = gpa >> PAGE_SHIFT;
+    mfn_t mfn;
+    p2m_type_t p2mt;
 
-    /*
-     * If this GFN is emulated MMIO or marked as read-only, pass the fault
-     * to the mmio handler.
-     */
-    mfn = gfn_to_mfn_type_current(gfn, &p2mt, p2m_guest);
-    if ( (p2mt == p2m_mmio_dm) || (p2mt == p2m_ram_ro) )
-    {
-        if ( !handle_mmio() )
-            hvm_inject_exception(TRAP_gp_fault, 0, 0);
-        return;
-    }
-
-    /* Log-dirty: mark the page dirty and let the guest write it again */
-    if ( p2mt == p2m_ram_logdirty )
-    {
-        paging_mark_dirty(current->domain, mfn_x(mfn));
-        p2m_change_type(current->domain, gfn, p2m_ram_logdirty, p2m_ram_rw);
-        return;
-    }
-
-    /* Okay, this shouldn't happen.  Maybe the guest was writing to a
-       read-only grant mapping? */
-    if ( p2mt == p2m_grant_map_ro )
-    {
-        /* Naughty... */
-        gdprintk(XENLOG_WARNING,
-                 "trying to write to read-only grant mapping\n");
-        hvm_inject_exception(TRAP_gp_fault, 0, 0);
+    if ( hvm_hap_nested_page_fault(gfn) )
         return;
-    }
 
-    /* Something bad has happened; either Xen or the hardware have
-       screwed up. */
-    gdprintk(XENLOG_WARNING, "unexpected SVM nested page fault\n");
+    /* Everything else is an error. */
+    mfn = gfn_to_mfn_type_current(gfn, &p2mt, p2m_guest);
+    gdprintk(XENLOG_ERR, "SVM violation gpa %#"PRIpaddr", mfn %#lx, type %i\n",
+             gpa, mfn_x(mfn), p2mt);
+    domain_crash(current->domain);
 }
 
 static void svm_fpu_dirty_intercept(void)
@@ -1511,7 +1485,7 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs)
     case VMEXIT_NPF:
         perfc_incra(svmexits, VMEXIT_NPF_PERFC);
         regs->error_code = vmcb->exitinfo1;
-        svm_do_nested_pgfault(vmcb->exitinfo2, regs);
+        svm_do_nested_pgfault(vmcb->exitinfo2);
         break;
 
     case VMEXIT_IRET:
index d85b3d877b92530220016bb9a6b96e5990f61397..f903abe49cdbbeb0b2fbb697d7e314fa053666a8 100644 (file)
@@ -2153,50 +2153,16 @@ static void vmx_wbinvd_intercept(void)
 
 static void ept_handle_violation(unsigned long qualification, paddr_t gpa)
 {
-    unsigned long gla_validity = qualification & EPT_GLA_VALIDITY_MASK;
-    struct domain *d = current->domain;
     unsigned long gla, gfn = gpa >> PAGE_SHIFT;
     mfn_t mfn;
-    p2m_type_t t;
-
-    mfn = gfn_to_mfn_guest(d, gfn, &t);
-
-    /* There are three legitimate reasons for taking an EPT violation. 
-     * One is a guest access to MMIO space. */
-    if ( gla_validity == EPT_GLA_VALIDITY_MATCH && p2m_is_mmio(t) )
-    {
-        handle_mmio();
-        return;
-    }
-
-    /* The second is log-dirty mode, writing to a read-only page;
-     * The third is populating a populate-on-demand page. */
-    if ( (gla_validity == EPT_GLA_VALIDITY_MATCH
-          || gla_validity == EPT_GLA_VALIDITY_GPT_WALK)
-         && p2m_is_ram(t) && (t != p2m_ram_ro) )
-    {
-        if ( paging_mode_log_dirty(d) )
-        {
-            paging_mark_dirty(d, mfn_x(mfn));
-            p2m_change_type(d, gfn, p2m_ram_logdirty, p2m_ram_rw);
-            flush_tlb_mask(&d->domain_dirty_cpumask);
-        }
-        return;
-    }
+    p2m_type_t p2mt;
 
-    /* Ignore writes to:
-     *     1. read only memory regions;
-     *     2. memory holes. */
-    if ( (qualification & EPT_WRITE_VIOLATION)
-         && (((gla_validity == EPT_GLA_VALIDITY_MATCH) && (t == p2m_ram_ro))
-             || (mfn_x(mfn) == INVALID_MFN)) ) {
-        int inst_len = __get_instruction_length();
-        __update_guest_eip(inst_len);
+    if ( (qualification & EPT_GLA_VALID) &&
+         hvm_hap_nested_page_fault(gfn) )
         return;
-    }
 
     /* Everything else is an error. */
-    gla = __vmread(GUEST_LINEAR_ADDRESS);
+    mfn = gfn_to_mfn_type_current(gfn, &p2mt, p2m_guest);
     gdprintk(XENLOG_ERR, "EPT violation %#lx (%c%c%c/%c%c%c), "
              "gpa %#"PRIpaddr", mfn %#lx, type %i.\n", 
              qualification, 
@@ -2206,29 +2172,20 @@ static void ept_handle_violation(unsigned long qualification, paddr_t gpa)
              (qualification & EPT_EFFECTIVE_READ) ? 'r' : '-',
              (qualification & EPT_EFFECTIVE_WRITE) ? 'w' : '-',
              (qualification & EPT_EFFECTIVE_EXEC) ? 'x' : '-',
-             gpa, mfn_x(mfn), t);
-
-    if ( qualification & EPT_GAW_VIOLATION )
-        gdprintk(XENLOG_ERR, " --- GPA too wide (max %u bits)\n", 
-                 9 * (unsigned) d->arch.hvm_domain.vmx.ept_control.gaw + 21);
+             gpa, mfn_x(mfn), p2mt);
 
-    switch ( gla_validity )
+    if ( qualification & EPT_GLA_VALID )
     {
-    case EPT_GLA_VALIDITY_PDPTR_LOAD:
-        gdprintk(XENLOG_ERR, " --- PDPTR load failed\n"); 
-        break;
-    case EPT_GLA_VALIDITY_GPT_WALK:
-        gdprintk(XENLOG_ERR, " --- guest PT walk to %#lx failed\n", gla);
-        break;
-    case EPT_GLA_VALIDITY_RSVD:
-        gdprintk(XENLOG_ERR, " --- GLA_validity 2 (reserved)\n");
-        break;
-    case EPT_GLA_VALIDITY_MATCH:
-        gdprintk(XENLOG_ERR, " --- guest access to %#lx failed\n", gla);
-        break;
+        gla = __vmread(GUEST_LINEAR_ADDRESS);
+        gdprintk(XENLOG_ERR, " --- GLA %#lx\n", gla);
     }
 
-    domain_crash(d);
+    if ( qualification & EPT_GAW_VIOLATION )
+        gdprintk(XENLOG_ERR, " --- GPA too wide (max %u bits)\n", 
+                 9 * (unsigned int)current->domain->arch.hvm_domain.
+                 vmx.ept_control.gaw + 21);
+
+    domain_crash(current->domain);
 }
 
 static void vmx_failed_vmentry(unsigned int exit_reason,
index 2ee9b81a06dff12e26b67bd6c7d25ea5655542d8..cbff2e0a0d5b2713d02ee82d1557650b3bfbb3df 100644 (file)
@@ -333,4 +333,6 @@ static inline void hvm_set_info_guest(struct vcpu *v)
 
 int hvm_debug_op(struct vcpu *v, int32_t op);
 
+bool_t hvm_hap_nested_page_fault(unsigned long gfn);
+
 #endif /* __ASM_X86_HVM_HVM_H__ */
index 2c248bf0436b85dd113ce96be74be96e46dd29a9..ddda6c09aa623a3a298efb9efece4bffa1978363 100644 (file)
@@ -366,45 +366,24 @@ void vmx_inject_nmi(void);
 void ept_p2m_init(struct domain *d);
 
 /* EPT violation qualifications definitions */
-/* bit offset 0 in exit qualification */
 #define _EPT_READ_VIOLATION         0
 #define EPT_READ_VIOLATION          (1UL<<_EPT_READ_VIOLATION)
-/* bit offset 1 in exit qualification */
 #define _EPT_WRITE_VIOLATION        1
 #define EPT_WRITE_VIOLATION         (1UL<<_EPT_WRITE_VIOLATION)
-/* bit offset 2 in exit qualification */
 #define _EPT_EXEC_VIOLATION         2
 #define EPT_EXEC_VIOLATION          (1UL<<_EPT_EXEC_VIOLATION)
-
-/* bit offset 3 in exit qualification */
 #define _EPT_EFFECTIVE_READ         3
 #define EPT_EFFECTIVE_READ          (1UL<<_EPT_EFFECTIVE_READ)
-/* bit offset 4 in exit qualification */
 #define _EPT_EFFECTIVE_WRITE        4
 #define EPT_EFFECTIVE_WRITE         (1UL<<_EPT_EFFECTIVE_WRITE)
-/* bit offset 5 in exit qualification */
 #define _EPT_EFFECTIVE_EXEC         5
 #define EPT_EFFECTIVE_EXEC          (1UL<<_EPT_EFFECTIVE_EXEC)
-
-/* bit offset 6 in exit qualification */
 #define _EPT_GAW_VIOLATION          6
 #define EPT_GAW_VIOLATION           (1UL<<_EPT_GAW_VIOLATION)
-
-/* bits offset 7 & 8 in exit qualification */
-#define _EPT_GLA_VALIDITY           7
-#define EPT_GLA_VALIDITY_MASK       (3UL<<_EPT_GLA_VALIDITY)
-/* gla != gpa, when load PDPTR */
-#define EPT_GLA_VALIDITY_PDPTR_LOAD (0UL<<_EPT_GLA_VALIDITY)
-/* gla != gpa, during guest page table walking */
-#define EPT_GLA_VALIDITY_GPT_WALK   (1UL<<_EPT_GLA_VALIDITY)
-/* reserved */
-#define EPT_GLA_VALIDITY_RSVD       (2UL<<_EPT_GLA_VALIDITY)
-/* gla == gpa, normal case */
-#define EPT_GLA_VALIDITY_MATCH      (3UL<<_EPT_GLA_VALIDITY)
-
-#define EPT_EFFECTIVE_MASK          (EPT_EFFECTIVE_READ  |  \
-                                     EPT_EFFECTIVE_WRITE |  \
-                                     EPT_EFFECTIVE_EXEC)
+#define _EPT_GLA_VALID              7
+#define EPT_GLA_VALID               (1UL<<_EPT_GLA_VALID)
+#define _EPT_GLA_FAULT              8
+#define EPT_GLA_FAULT               (1UL<<_EPT_GLA_FAULT)
 
 #define EPT_PAGETABLE_ENTRIES       512